﻿using crypto_all.SM;
using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Encoders;
using System;
using System.Text;
using System.Threading;

namespace crypto_all
{
    class Program
    {
        static void Main(string[] args)
        {
            string plainText = "Hello,World!";
            Console.WriteLine("原始数据 : " + plainText);
            TestSM2(plainText);

            var a1 = TestSM3(plainText);
            System.Console.Out.WriteLine("哈希a1:" + a1);
            Thread.Sleep(1000);
            var a2 = TestSM3(plainText);
            System.Console.Out.WriteLine("哈希a2:" + a2);
            if (a1 == a2)
            {
                System.Console.Out.WriteLine("哈希验证通过");
            }
            TestSM4(plainText);

            TestSM2Sign_Verify();

            Console.ReadLine();
        }

        private static void TestSM2(string plainText)
        {
            System.Console.Out.WriteLine("\n--- SM2加解密算法 ----------------------------");
            var tuple = GenerateKeyPair();

            byte[] sourceData = Encoding.Default.GetBytes(plainText);

            //下面的秘钥可以使用generateKeyPair()生成的秘钥内容  
            // 国密规范正式私钥  
            String prik = tuple.Item2;
            // 国密规范正式公钥  
            String pubk = tuple.Item1;

            System.Console.Out.WriteLine("加密: ");
            String cipherText = SM2Utils.Encrypt(HexStringToByteArray(pubk), sourceData);
            System.Console.Out.WriteLine(cipherText);
            System.Console.Out.WriteLine("解密: ");
            plainText = Encoding.Default.GetString(SM2Utils.Decrypt(HexStringToByteArray(prik), HexStringToByteArray(cipherText)));
            System.Console.Out.WriteLine(plainText);
        }

        private static void TestSM2Sign_Verify()
        {
            System.Console.Out.WriteLine("\n--- 数字签名算法 ----------------------------");
            AsymmetricCipherKeyPair keypair = SM2Utils.GenerateKeyPair();

            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)keypair.Private;
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters)keypair.Public;

            System.String msg = "message digest";
            
            var sign = SM2Utils.Sign(ecpriv, Encoding.Default.GetBytes(msg));

            var ret = SM2Utils.Verify(ecpub, Encoding.Default.GetBytes(msg), sign);
            System.Console.Out.WriteLine("验签结果：" + ret);
        }

        private static string TestSM3(string plainText) 
        {
            System.Console.Out.WriteLine("\n--- SM3加解密算法 ----------------------------");
            byte[] md = new byte[32];
            byte[] msg1 = Encoding.Default.GetBytes(plainText);
            SM3Digest sm3 = new SM3Digest();
            sm3.BlockUpdate(msg1, 0, msg1.Length);
            sm3.DoFinal(md, 0);
            string s = new UTF8Encoding().GetString(Hex.Encode(md));

            return s.ToUpper();
        }
        private static void TestSM4(string plainText)
        {
            SM4Utils sM4Utils = new SM4Utils();
            sM4Utils.secretKey = "JeF8U9wHFOMfs2Y8";
            sM4Utils.hexString = false;
            string tempiv = GetRandomString(16, true, true, true, false, null).ToLower();
            sM4Utils.iv = tempiv;

            string cipherText_CBC = sM4Utils.Encrypt_CBC(plainText);// 安全性高于ECB，不支持并行计算
            System.Console.Out.WriteLine("加密CBC: " + cipherText_CBC);
            string cipherText_ECB = sM4Utils.Encrypt_ECB(plainText);// 支持并行计算
            System.Console.Out.WriteLine("加密ECB: " + cipherText_ECB);

            var plainText_CBC = sM4Utils.Decrypt_CBC(cipherText_CBC);
            System.Console.Out.WriteLine("解密CBC: " + plainText_CBC);
            var plainText_ECB = sM4Utils.Decrypt_ECB(cipherText_ECB);
            System.Console.Out.WriteLine("解密ECB: " + plainText_ECB);
        }
        private static ValueTuple<string,string> GenerateKeyPair()
        {
            AsymmetricCipherKeyPair key = SM2Utils.GenerateKeyPair();
            ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters)key.Private;
            ECPublicKeyParameters ecpub = (ECPublicKeyParameters)key.Public;
            BigInteger privateKey = ecpriv.D;
            ECPoint publicKey = ecpub.Q;

            string pubk = Encoding.ASCII.GetString(Hex.Encode(publicKey.GetEncoded())).ToUpper();
            string prik = Encoding.ASCII.GetString(Hex.Encode(privateKey.ToByteArray())).ToUpper();
            System.Console.Out.WriteLine("公钥: " + pubk);
            System.Console.Out.WriteLine("私钥: " + prik);
            return (pubk, prik);
        }
        private static byte[] HexStringToByteArray(string s)
        {
            s = s.Replace(" ", "");
            byte[] buffer = new byte[s.Length / 2];
            for (int i = 0; i < s.Length; i += 2)
            {
                buffer[i / 2] = (byte)Convert.ToByte(s.Substring(i, 2), 16);
            }

            return buffer;
        }
        ///<summary>
        ///生成随机字符串 
        ///</summary>
        ///<param name="length">目标字符串的长度</param>
        ///<param name="useNum">是否包含数字，1=包含，默认为包含</param>
        ///<param name="useLow">是否包含小写字母，1=包含，默认为包含</param>
        ///<param name="useUpp">是否包含大写字母，1=包含，默认为包含</param>
        ///<param name="useSpe">是否包含特殊字符，1=包含，默认为不包含</param>
        ///<param name="custom">要包含的自定义字符，直接输入要包含的字符列表</param>
        ///<returns>指定长度的随机字符串</returns>
        private static string GetRandomString(int length, bool useNum, bool useLow, bool useUpp, bool useSpe, string custom)
        {
            byte[] b = new byte[4];
            new System.Security.Cryptography.RNGCryptoServiceProvider().GetBytes(b);
            Random r = new Random(BitConverter.ToInt32(b, 0));
            string s = null, str = custom;
            if (useNum == true) { str += "0123456789"; }
            if (useLow == true) { str += "abcdefghijklmnopqrstuvwxyz"; }
            if (useUpp == true) { str += "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; }
            if (useSpe == true) { str += "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"; }
            for (int i = 0; i < length; i++)
            {
                s += str.Substring(r.Next(0, str.Length - 1), 1);
            }
            return s;
        }
    }
}
